#include "avpacketq.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/time.h>
#include <stdatomic.h>

struct avpacketq_info
{
    atomic_int write_index;
    void* data;
    encoder_info_t encoder_info;
    atomic_uint encoder_info_isready;
};

avpacketq_t* avpacketq_new(char const* name, int count, uint32_t avpacket_size) {
    avpacketq_t* self;
    avpacketq_info_t* avpacketq_info;
    bool created;

    self = (avpacketq_t*) malloc(sizeof(avpacketq_t));
    if (self == NULL) {
        return NULL;
    }

    self->max_count = count;
    self->avpacket_size = avpacket_size;
    self->max_size = count * avpacket_size;
    self->name = strdup(name);
    self->mmap_size = sizeof(avpacketq_info_t) + ECONDER_EXTRA_DATA_LEN + self->max_count * sizeof(avpacket_info_t) + self->max_size - 1;
    self->mmap_size = ((self->mmap_size + 4096) / 4096) * 4096; // PAGE_SIZE align, 4KB 页对齐

    created = false;
    self->shmem_fd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
    if (self->shmem_fd == -1) {
        if (errno == ENOENT) {
            self->shmem_fd = shm_open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
            if (self->shmem_fd == -1) {
                goto FAIL;
            }
            created = true;
        }
        else {
            goto FAIL;
        }
    }
    // MP_LOG_DBG(" initialized queue %s, created = %d mmap_size=%u \n", name,
    // created, self->mmap_size);
    if (created && (-1 == ftruncate(self->shmem_fd, self->mmap_size)))
        goto FAIL;

    self->mem = (avpacketq_info_t*) mmap(NULL, self->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, self->shmem_fd, 0);
    if (self->mem == MAP_FAILED)
        goto FAIL;

    avpacketq_info = self->mem;
    void* p_tmp = (void*) self->mem;

    avpacketq_info->encoder_info.encoder_extra.data = p_tmp + sizeof(avpacketq_info_t);
    if (created) {
        avpacketq_info->encoder_info.encoder_extra.len = ECONDER_EXTRA_DATA_LEN;
        // self->mem->read_index = self->mem->write_index = 0;
        atomic_init(&self->mem->encoder_info_isready, 0);
        // atomic_init(&self->mem->read_index, 0);
        atomic_init(&self->mem->write_index, -1);
    }

    return self;

FAIL:
    if (self->shmem_fd != -1) {
        fprintf(stderr, " FAIL \n");
        close(self->shmem_fd);
        shm_unlink(self->name);
    }
    free(self->name);
    free(self);
    return NULL;
}

int avpacketq_enqueue(avpacketq_t* self, avpacket_info_t* in_avpacket_info) {
    int ret;
    int w_index;
    int r_index;

    avpacket_info_t* avpacket_info;
    uint8_t* p_avpacket_data;
    uint8_t* buffer_in;
    int in_len;

    buffer_in = in_avpacket_info->data;
    in_len = in_avpacket_info->data_len;
    ret = in_len;

    p_avpacket_data = (void*) self->mem + sizeof(avpacketq_info_t) + ECONDER_EXTRA_DATA_LEN + self->max_count * sizeof(avpacket_info_t);
    // r_index = self->mem->read_index;
    w_index = atomic_load(&self->mem->write_index);

    avpacket_info = (avpacket_info_t*) ((void*) self->mem + sizeof(avpacketq_info_t) + ECONDER_EXTRA_DATA_LEN + ((w_index + 1) % self->max_count) * sizeof(avpacket_info_t));


    if (in_len <= self->avpacket_size) {
        // Copy avpacket_info_t
        memcpy((void*) avpacket_info, (void*) in_avpacket_info, sizeof(avpacket_info_t));
        // Copy the encoded packet data
        p_avpacket_data += ((w_index + 1) % self->max_count) * self->avpacket_size;
        memcpy((void*) p_avpacket_data, buffer_in, in_len);
        avpacket_info->data_len = in_len;
        atomic_store(&self->mem->write_index, (w_index + 1) % self->max_count); //(self->mem->write_index + 1) % self->max_count;
        // MP_LOG_DBG("memcpy p_avpacket_data in_len is %d \n", in_len);
        ret = in_len;
    }
    else {
        ret = -1;
    }
    return ret;
}


int avpacketq_dequeue(avpacketq_t* self, avpacket_info_t* out_avpacket_info) {
    int ret;
    int w_index;
    int r_index;
    avpacket_info_t* avpacket_info;
    uint8_t* p_avpacket_data;
    uint8_t* buffer_out;
    int out_len;

    r_index = out_avpacket_info->rd_index;
    w_index = atomic_load(&self->mem->write_index);
    if (w_index == -1) {
        return -1;
    }
    if (r_index == -1) {
        out_avpacket_info->rd_index = w_index;
        return -2;
    }

    if (r_index == w_index) {
        return -3;
    }

    buffer_out = (uint8_t*) out_avpacket_info->data;
    out_len = out_avpacket_info->data_len;

    p_avpacket_data = (void*) self->mem + sizeof(avpacketq_info_t) + ECONDER_EXTRA_DATA_LEN + self->max_count * sizeof(avpacket_info_t);

    // MP_LOG_DBG(" dequeue w_index = %d r_index =%d m_attatched=%d max_count=%d
    // \n" , w_index, r_index, self->mem->m_attatched, self->max_count);

    avpacket_info = (avpacket_info_t*) ((void*) self->mem + sizeof(avpacketq_info_t) +
        ECONDER_EXTRA_DATA_LEN + r_index * sizeof(avpacket_info_t));

    ret = out_len;

    if (avpacket_info->data_len <= out_len) {
        // Copy avpacket_info_t
        memcpy((void*) out_avpacket_info, (void*) avpacket_info, sizeof(avpacket_info_t));
        uint8_t* r_data = (uint8_t*) avpacket_info + sizeof(avpacket_info_t);
        // Copy the encoded packet data
        p_avpacket_data += r_index * self->avpacket_size;

        memcpy((void*) buffer_out, (void*) p_avpacket_data, avpacket_info->data_len);

        out_avpacket_info->data = buffer_out;
        out_avpacket_info->rd_index = (r_index + 1) % self->max_count;

        ret = avpacket_info->data_len;
    }
    else {
        ret = 0;
    }

    return ret;
}


// void avpacketq_destroy(avpacketq_t* self, int unlink) {
//     if (!self) {
//         return;
//     }

//     munmap(self->mem, self->max_size);
//     close(self->shmem_fd);
//     // if (unlink) {
//     //     shm_unlink(self->name);
//     // }
//     if (self->name) {
//         free(self->name);
//         self->name = NULL;
//     }
//     if (self) {
//         free(self);
//         self = NULL;
//     }

//     MP_LOG_DBG(" avpacketq_destroy ok\n");
// }

void avpacketq_destroy(avpacketq_t** self, int unlink) {
    if (!self || !(*self)) {
        return;
    }

    munmap((*self)->mem, (*self)->max_size);
    close((*self)->shmem_fd);
    // if (unlink) {
    //     shm_unlink(self->name);
    // }
    if ((*self)->name) {
        free((*self)->name);
        (*self)->name = NULL;
    }
    if (*self) {
        free(*self);
        *self = NULL;
    }

    //MP_LOG_DBG(" avpacketq_destroy ok\n");
    fprintf(stderr, " avpacketq_destroy ok\n");
}

int avpacketq_set_encoder_info(avpacketq_t* self, encoder_info_t* encoder_info) {
    if (self == NULL || encoder_info == NULL) {
        return -1;
    }
    memcpy((void*) &self->mem->encoder_info.encoder_param, (void*) &encoder_info->encoder_param, sizeof(encoder_param_t));
    if (encoder_info->encoder_extra.data != NULL) {
        memcpy((void*) self->mem->encoder_info.encoder_extra.data, (void*) encoder_info->encoder_extra.data, encoder_info->encoder_extra.len);
        self->mem->encoder_info.encoder_extra.len = encoder_info->encoder_extra.len;
    }
    else {
        self->mem->encoder_info.encoder_extra.len = 0;
    }

    atomic_store(&self->mem->encoder_info_isready, 1);
    return 0;
}

int avpacketq_get_encoder_info(avpacketq_t* self, encoder_info_t* encoder_info) {
    if (self == NULL || encoder_info == NULL) {
        return -1;
    }
    if (self->mem->encoder_info.encoder_extra.len != 0) {
        memcpy((void*) encoder_info->encoder_extra.data, (void*) self->mem->encoder_info.encoder_extra.data, self->mem->encoder_info.encoder_extra.len);
        encoder_info->encoder_extra.len = self->mem->encoder_info.encoder_extra.len;
    }
    else {
        encoder_info->encoder_extra.len = 0;
    }
    int ret = atomic_load(&self->mem->encoder_info_isready);
    if (ret == 1) {
        memcpy((void*) &encoder_info->encoder_param, (void*) &self->mem->encoder_info.encoder_param, sizeof(encoder_param_t));
        return 0;
    }
    return -2;
}